﻿using System;
using System.Collections.Generic;

namespace Simp.CodeModel.CoreVisitors
{
	/// <summary>
	/// Visitor visiting a function tree and identifying function parameters as parameters (unbound function parameters)
	/// in the custom function bodies.
	/// </summary>
	public class IdentifyUnboundParameterVisitor : IFunctionVisitor
	{
		private readonly string[] _parameterNameList;

		/// <summary>Default parameters.</summary>
		public IdentifyUnboundParameterVisitor()
			: this(FunctionHelper.EmptyParameterNames)
		{
		}

		/// <summary>Private constructor taking parameter's name in parameter.</summary>
		/// <param name="parameterNameList"></param>
		private IdentifyUnboundParameterVisitor(string[] parameterNameList)
		{
			_parameterNameList = parameterNameList;
		}

		#region IFunctionVisitor Members
		IFunction IFunctionVisitor.Visit(IFunction function)
		{
			CustomFunction customFunction = function as CustomFunction;

			if (customFunction != null)
			{
				return VisitCustomFunction(customFunction);
			}
			else
			{
				UnboundFunctionCalling unbound = function as UnboundFunctionCalling;

				if (unbound != null)
				{
					if (unbound.IsParameter || !((IList<string>)_parameterNameList).Contains(unbound.FunctionName))
					{	//	Already a parameter or isn't a parameter's name
						return function.AcceptVisitor(this);
					}
					else
					{
						return new UnboundFunctionCalling(unbound.FunctionName, true, unbound.FunctionParameters);
					}
				}
				else
				{	//	Dig into the function
					return function.AcceptVisitor(this);
				}
			}
		}
		#endregion

		private CustomFunction VisitCustomFunction(CustomFunction customFunction)
		{	//	Merge current parameter list with the custom function's parameters
			List<string> mergedList = new List<string>();

			mergedList.AddRange(_parameterNameList);
			mergedList.AddRange(((IFunction)customFunction).ParameterNames);

			IFunctionVisitor newVisitor = new IdentifyUnboundParameterVisitor(mergedList.ToArray());
			IFunction visitedBody = newVisitor.Visit(customFunction.FunctionBody);

			//	We don't want to touch to the default values...  yet

			if (visitedBody != null)
			{
				return new CustomFunction(
					visitedBody,
					((IFunction)customFunction).FunctionName,
					((IFunction)customFunction).ParameterNames,
					((IFunction)customFunction).DefaultParameterValues);
			}
			else
			{
				return null;
			}
		}
	}
}